/*! \file  operators.h
	\author HiddenSeeker
    Declares an unary and binary operator
*/
#include "../abstractnode.h"
#include <string>
#pragma once

class MethodSelectDelegate;
class ParentData;
class CallInformation;
class OperatorNode;
class CurrentOperatorPtr;
/*! Basic class for context dependent operator actions
*/
class OperatorCommand
{
 protected:
	  std::string m_type; //!< Used type
 public:
		 virtual bool transformCode(ParentData * data);
		 /*! Transforms from assignment
		 */
		 virtual bool canBeTransformedFromAssignment(ParentData * data, bool * error);
	     /*! Transforms a code, if needed to
			\return true on success
		 */
		 virtual bool checkLogic(ParentData * data); 
		 virtual ~OperatorCommand();

		 virtual bool isResultReturn(OperatorNode * node);

		 /*! Checks common errors
		  */
		 virtual void checkCommonErrors(ParentData * data);
		 /*! Returns unknown type
		 */
		 virtual std::string getType(ParentData * data);
		 /*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);

		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
};

class ArrayGetCommand : public OperatorCommand
{
  private:
		  CallInformation * m_call_delegate;
		  /*! String of class, where we should convert
		   */
		  std::string m_conversion_class;
		  /*! When we compiling array get, we should perform checked cast to return valid type
		   */
		  bool        m_checked_cast;
		  /*! Checked class perform
		   */
		  unsigned short m_check_class;
  public:
	     /*! Return true
		  */
	     bool canBeTransformedFromAssignment(ParentData * data, bool * error);
		  /*! Checks common errors
		  */
		 virtual void checkCommonErrors(ParentData * data);
	     /*! Fills a constant table for method
		  */
		 virtual void fillConstant(ParentData * data);
		 /*! Produces a code for method
		  */
		 virtual void produceCode(ParentData * data);

		 inline ArrayGetCommand() 
		 {
			 m_call_delegate=NULL;
			 m_checked_cast=false;
		 }
		 ~ArrayGetCommand();
};

class  CommonOperatorCommand : public OperatorCommand
{
 private:
		  CallInformation * m_call_delegate;
 public:
		inline CommonOperatorCommand()
		{
			m_call_delegate=NULL;
		}
		/*! Checks common errors
		 */
		virtual void checkCommonErrors(ParentData * data);
		/*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);
		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
		~CommonOperatorCommand();
};

class ClassSemantic;
class SemanticVisitorData;

class CompareCommand: public OperatorCommand
{
 private:
			class CheckArgumentData //Temprary checked argument data
 			{
			 public:
			         AbstractNode    * node;     // A node
			         ClassSemantic   * sclass;   // Semantic class
			         std::string       type;     // Type of class
			};
			/*! A data for checking arguments
			 */
			CheckArgumentData   data[2];
			/*! Used operator ptr
			 */
			OperatorNode      * myoperator;
			/*! Classes, used for scanning
			 */
			SemanticVisitorData      * classes;
			/*! Used parent data
			 */ 
			ParentData               * pdata;
			/*! Sets a default comparison for command
			 */
			void setDefaultComparison();
			/*! This function is called when one or both types are invalid.
				Used to handle VOID calls
			 */
			void handleInvalidTypes();
			/*! If all types is used this should be used to perform actions
			 */
			void handleCommonCall();
			/*! A vector for delegates
			 */
			std::vector<MethodSelectDelegate *> m_delegates;
protected:
	        /*! Default comparison for creation
			 */
			MethodSelectDelegate * m_default_comparison; 
public:
			CompareCommand()
			{
				m_default_comparison=NULL;
			}
			/*! Checks common errors
			 */
			virtual void checkCommonErrors(ParentData * data);
			/*! Fills a constant table for method
			 */
			virtual void fillConstant(ParentData * data);
			/*! Produces a code for method
			 */
			virtual void produceCode(ParentData * data);
			~CompareCommand();
};
class EqualityCommand: public CompareCommand
{
 protected:

 public:
			EqualityCommand();	
			~EqualityCommand();
};
class NonEqualityCommand: public CompareCommand
{
 protected:
 public:
			NonEqualityCommand();
			~NonEqualityCommand();
};

class EqCommand: public EqualityCommand
{
 public:
		inline EqCommand()
		{
		}
};

class NeqCommand: public NonEqualityCommand
{
 public:
		inline NeqCommand()
		{
		}
};

class TildaCommand: public EqualityCommand
{
 public:
		inline TildaCommand()
		{
		}
};
class NtildaCommand: public NonEqualityCommand
{
 public:
		inline NtildaCommand()
		{
		}
};
class NotCommand: public OperatorCommand
{
 private:
		CallInformation * m_call_delegate;
 public:
		inline NotCommand()
		{
			m_call_delegate=NULL;
		}
		~NotCommand();
		/*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);
		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
		/*! Checks common errors
		 */
		virtual void checkCommonErrors(ParentData * data);
};

class AssignmentCommand: public OperatorCommand
{
 private:
	   /*! Index of local variable in data
	    */
	   unsigned char  m_local_index;
	   /*! Second operator conversion delegates
	    */
	   std::vector<MethodSelectDelegate *> m_delegates;
 public:
	    /*! Performs transformations
		 */
	    bool transformCode(ParentData * data);
	    inline AssignmentCommand()
		{
		}
		 /*! Checks common errors
		  */
		 virtual void checkCommonErrors(ParentData * data);
		/*! Transforms a code, if needed to
			\return true on success
		 */
		 virtual bool checkLogic(ParentData * data); 
	    /*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);

		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
		/*! Test if it's returns result
		 */
		bool isResultReturn(OperatorNode * node);
	    ~AssignmentCommand();
};

class FieldAssignment: public OperatorCommand
{
 private:
	     std::vector<MethodSelectDelegate *> m_delegates;
		 unsigned short m_fieldref;
 public:
	    inline FieldAssignment()
		{
		}
		/*! Checks common errors
		  */
		virtual void checkCommonErrors(ParentData * data);
		/*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);

		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
		~FieldAssignment();
		/*! Transforms a code, if needed to
			\return true on success
		 */
		 virtual bool checkLogic(ParentData * data); 
};
class ArrayAssignment: public OperatorCommand
{
 private:
		CallInformation * m_call_delegate;
		bool m_pop_result;
 public:
	    inline ArrayAssignment()
		{
			m_call_delegate=NULL;
			m_pop_result=false;
		}
		~ArrayAssignment();
		/*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);

		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);

		void checkCommonErrors(ParentData * data);
		/*! Transforms a code, if needed to
			\return true on success
		 */
		 virtual bool checkLogic(ParentData * data); 
};

class OperatorNode: public AbstractNode
{
 private:
	     std::vector<AbstractNode *>  m_args;
		 std::string                  m_name;
		 OperatorCommand          *   m_behaviour;

		 OperatorCommand          *   m_transformation_behaviour;
		 /*! Whether node type was determined
		  */
		 bool m_visited;
 public:
	    inline const std::string & name()
	    {
			return m_name;
		}
	    /*! Transforms from assignment
		 */
		virtual bool canBeTransformedFromAssignment(ParentData * data, bool * error);
		/*! Literal name
		 */
		inline void setLiteralName(const std::string & name)
		{
			m_name=name;
		}
		inline std::vector<AbstractNode *> & args()
		{
			return m_args;
		}
		inline void setTransformationBehaviour(OperatorCommand * cmd)
		{
			m_transformation_behaviour=cmd;
		}
		/*! Checks if returning result
     	  */
	    virtual bool   isResultReturn();
		/*! Transforms a code, if needed to
			\return true on success
		 */
		virtual bool checkLogic(ParentData * data); 
	    inline OperatorNode(const YYLTYPE & loc,AbstractNode * arg, const std::string & name): AbstractNode(loc)
	    {
			m_name=name;
			m_args.push_back(arg);
			if (m_name=="not")
				m_behaviour=new NotCommand();
			else
				m_behaviour=new CommonOperatorCommand();
			m_transformation_behaviour=NULL;
			m_visited=false;
	    }
		inline OperatorNode(const YYLTYPE & loc,AbstractNode * arg1,AbstractNode * arg2, const std::string & name): AbstractNode(loc)
	    {
			m_name=name;
			m_args.push_back(arg1);
			m_args.push_back(arg2);
			m_behaviour=NULL;
			if (m_name==":=")
				m_behaviour=new AssignmentCommand();
			else if (m_name=="[]")
				m_behaviour=new ArrayGetCommand();
			else if (m_name=="=") 
				m_behaviour=new EqCommand();
			else if (m_name=="/=")
				m_behaviour=new NeqCommand();
			else if (m_name=="~")
				m_behaviour=new TildaCommand();
			else if (m_name=="/~")
				m_behaviour=new NtildaCommand();
			else	m_behaviour=new CommonOperatorCommand();
			m_transformation_behaviour=NULL;
			m_visited=false;
	    }
		/*! Checks common errors
		  */
		virtual void checkCommonErrors(ParentData * data);
		 /*! Returns unknown type
		 */
		virtual std::string getType(ParentData * data);
		/*! Transforms a code, if needed to
			\return true on success
		 */
		virtual bool transformCode(ParentData * data);
		/*! Returns false
		 */
		virtual bool isOperator();
		/*! Fills a constant table for method
		 */
		virtual void fillConstant(ParentData * data);

		/*! Produces a code for method
		 */
		virtual void produceCode(ParentData * data);
	    /*! Writes an operator to stream
			\param[in] s stream
	     */
	    void  write(GraphStream * s);
	    
		virtual ~OperatorNode();
};
